import time
from copyreg import constructor

import requests
import json
import os
import sqlite3
from utils.db_utils import db_new_connection
from utils.config_utils import config_get_data_path

from datetime import datetime, timedelta
from urllib.parse import urlparse, parse_qs
import queue
import threading

request_queue = queue.Queue()
req_interval = 2

# 获取聊天列表
cookie = ""
headers={
    "User-Agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/100.0.4896.127 Safari/537.36",
    "cookie": cookie
}

savePath = config_get_data_path()



def select_qrcode():
    url = 'https://passport.bilibili.com/x/passport-login/web/qrcode/generate'
    resp = requests.get(url=url, headers=headers)
    text = resp.json()
    return text

def select_qrcode_key(qrcode_key = ''):
    url = 'https://passport.bilibili.com/x/passport-login/web/qrcode/poll?qrcode_key={}'.format(qrcode_key)
    resp = requests.get(url=url, headers=headers)
    text = resp.json()
    data = text['data']
    if data['code'] == 0:
        query_params = parse_qs(urlparse(data['url']).query)
        cookie_dict = {
            "DedeUserID": query_params.get("DedeUserID", [""])[0],
            "DedeUserID__ckMd5": query_params.get("DedeUserID__ckMd5", [""])[0],
            "SESSDATA": query_params.get("SESSDATA", [""])[0],
            "bili_jct": query_params.get("bili_jct", [""])[0],
            "buvid3": "",
            "buvid4": "",
            "buvid_fp": "",
            "b_nut": "",
            "bili_ticket": "",
            "b_lsid": "",
            "_uuid": "",
            "CURRENT_FNVAL": "4048"
        }
        cookie_str = "; ".join(f"{k}={v}" for k, v in cookie_dict.items() if v)
        return {
            "code": 200,
            "token": cookie_str
        }
    else:
        return data

# 获取目标用户信息
def select_main_user_message(cookiems = ''):
    headers['cookie'] = cookiems
    try:
        conn = db_new_connection()
        cursor = conn.cursor()
        url = 'https://api.bilibili.com/x/web-interface/nav'
        resp = requests.get(url=url, headers=headers)
        text = resp.json()
        code = text['code']
        print(headers)
        if code == 0:
            data = text['data']
            # 判断是否在目标表中
            cursor.execute('''
                SELECT * FROM mainUser WHERE mid = ?
                ''', (data['mid'],))
            mid = cursor.fetchone()
            if (mid):
                return data
            else:
                cursor.execute('''
                    INSERT INTO mainUser (name, mid, avatar)
                        VALUES (?, ?, ?)
                    ''', (data['uname'], data['mid'], data['face']))
                conn.commit()
                return data
        else:
            return False
    except requests.RequestException as e:
        return False
    except Exception as e:
        return False

# 获取聊天用户 每次20个,数量等于20往下查询,聊天用户判断是否在聊天表中,没有在添加, 每次获取请求聊天记录函数
def select_user(end_ts = None, mid = None):
    print("select_user",end_ts,mid)
    end_ts = None
    url = None

    talkerIdList = []
    while True:
        if(end_ts):
            url = "https://api.vc.bilibili.com/session_svr/v1/session_svr/get_sessions?session_type=1&group_fold=1&unfollow_fold=0&sort_rule=2&build=0&mobi_app=web&web_location=333.1296&w_rid=6ea9def6f1b246f94ea5e0ec52efa400&wts=1743408200&end_ts={}".format(
                end_ts)
        else:
            url = "https://api.vc.bilibili.com/session_svr/v1/session_svr/get_sessions?session_type=1&group_fold=1&unfollow_fold=0&sort_rule=2&build=0&mobi_app=web&web_location=333.1296&w_rid=6ea9def6f1b246f94ea5e0ec52efa400&wts=1743408200"

        temp_talker_array = []

        resp=requests.get(url=url,headers=headers)
        text=resp.json()
        messages = text['data']['session_list']
        count = len(messages) #获取私信数量

        for i in range(0, count):
            talker_id = messages[i]['talker_id']
            temp_talker_array.append(talker_id)

        if len(temp_talker_array) > 0:
            talkerIdList.extend(temp_talker_array)
            select_user_List(temp_talker_array, mid)
        else:
            print("私聊对象为空")

        if count == 20:
            end_ts = messages[19]['session_ts']
            time.sleep(req_interval)
        else:
            break


    len2 = len(talkerIdList)
    for i in range(0, len2):
        time.sleep(req_interval)
        selec_user_direct_message(talkerIdList[i], None, mid)


# 获取聊天用户基本信息,如果聊天用户表有信息不获取
def select_user_List(talkerIdList = [], mid = None):
    userMessage = []
    noUserMessage = []
    lenth = len(talkerIdList)
    for i in range(0,lenth):
        talkerId = select_single_user(talkerIdList[i])

        if (talkerId):
            userMessage.append(str(talkerIdList[i]))
        else:
            noUserMessage.append(str(talkerIdList[i]))

    if (len(noUserMessage) > 0):
        select_user_message(noUserMessage, mid)

# 聊天用户判断是否在聊天表中
def select_single_user(id = None):
    conn = db_new_connection()
    cursor = conn.cursor()

    cursor.execute('''
                SELECT talkerId FROM biLiUser WHERE talkerId = ?
            ''', (id,))
    talkerId = cursor.fetchone()
    return talkerId

# 聊天用户信息保存到表中
def select_user_message(userList = [], mid = None):
    conn = db_new_connection()
    cursor = conn.cursor()
    users = ','.join(userList)
    url = "https://api.bilibili.com/x/polymer/pc-electron/v1/user/cards?uids={}&build=0&mobi_app=web".format(users)
    userResp = requests.get(url=url, headers=headers)
    userText = userResp.json()
    userData = userText['data']
    lenth = len(userList)
    for i in range(0, lenth):
        id = userList[i]
        if id in userData:
            name = userData[id]['name']
            face = userData[id]['face']
            print("userData", id, name, face)
            cursor.execute('''
                            INSERT INTO biLiUser (name, talkerId, avatar, mid)
                            VALUES (?, ?, ?, ?)
                        ''', (name,id, face, mid))
            conn.commit()

# 获取聊天记录 每次20个,数量等于20往下查询,聊天记录判断是否在聊天记录表中,没有在添加,如果有之后的信息不再查询和请求有, 聊天记录保存到表中
def selec_user_direct_message(talkerId, end_seqno = None, mid = None):
    print("selec_user_direct_message")
    conn = db_new_connection()
    cursor = conn.cursor()
    if not end_seqno:
        url = "https://api.vc.bilibili.com/svr_sync/v1/svr_sync/fetch_session_msgs?talker_id={}&session_type=1&size=20&sender_device_id=1&build=0&mobi_app=web&web_location=333.1296&w_rid=1bb0acefed25cc87af853a9709e985ba&wts=1743406838".format(talkerId)
    else:
        url = "https://api.vc.bilibili.com/svr_sync/v1/svr_sync/fetch_session_msgs?talker_id={}&session_type=1&size=20&sender_device_id=1&build=0&mobi_app=web&web_location=333.1296&w_rid=1bb0acefed25cc87af853a9709e985ba&wts=1743406838&end_seqno={}".format(
            talkerId,end_seqno)
    resp = requests.get(url=url, headers=headers)
    text = resp.json()
    # print(text)
    # return
    messages = text['data']['messages']
    lenth = len(messages)  # 获取私信数量

    flag = True
    for i in range(0, lenth):
        msgKey = select_single_message(messages[i]['msg_key'])
        if(msgKey):

            flag = False
            return
        else:
            senderUid = messages[i]['sender_uid']
            receiverId =  messages[i]['receiver_id']
            content = messages[i]['content']
            msgSeqno = messages[i]['msg_seqno']
            timestamp = messages[i]['timestamp']
            msgType = messages[i]['msg_type']
            msgKey = messages[i]['msg_key']
            msgStatus = messages[i]['msg_status']
            msgSource = messages[i]['msg_source']
            cursor.execute('''
                INSERT INTO biLiMessage (mid, senderUid, receiverId, content,msgSeqno,timestamp,msgType,msgKey,msgStatus,msgSource)
                    VALUES (?, ?, ?, ?, ?, ?, ?, ?,?, ?)
                    ''', (mid, senderUid, receiverId, content,msgSeqno,timestamp,msgType,msgKey,msgStatus,msgSource))

            conn.commit()


            try:
                contentJSON = json.loads(content)
                if contentJSON.get("imageType"):
                    imageType = contentJSON.get("imageType")

                    if imageType in ('png', 'jpg'):
                        url = contentJSON.get("url")
                        imageName = url.split('/')
                        mid2 = None
                        if senderUid == mid:
                            mid2 = receiverId
                        else:
                            mid2 = senderUid

                        print("imageType", imageType, mid2)
                        if mid2 is not None:
                            obj = {
                                "mid": mid,
                                "senderUid": mid2,
                                "msgKey": msgKey,
                                "url": url,
                                "local_path": '{}/{}/{}'.format(mid, mid2, imageName[-1])
                            }
                            add_task(obj)
            except Exception as e:
                pass


    print(flag)
    if lenth == 20 and flag:
        time.sleep(req_interval)
        print("time.sleep")
        selec_user_direct_message(talkerId,messages[19]['msg_seqno'], mid)
# 聊天记录判断是否在聊天记录表中
def select_single_message(id = None):
    conn = db_new_connection()
    cursor = conn.cursor()

    cursor.execute('''
                SELECT msgKey FROM biLiMessage WHERE msgKey = ?
            ''', (id,))
    msgKey = cursor.fetchone()
    return msgKey


#图片下载
def download_image_with_retry(mid, senderUid,msgKey, url, local_path, max_retries=3, retry_delay=1):
    """
    下载网络图片保存到本地，并在失败时进行重试。

    :param url: 图片的网络地址
    :param local_path: 保存到本地的路径（包括文件名和扩展名）
    :param max_retries: 最大重试次数
    :param retry_delay: 每次重试之间的延迟（秒）
    """
    # 确保local_path的文件夹存在，如果不存在则创建
    file_path = savePath.replace("\\", "/")
    url_path = '{}bilibili/{}'.format(file_path, local_path)
    os.makedirs(os.path.dirname(url_path), exist_ok=True)

    attempt = 0
    while attempt < max_retries:
        try:
            # 发送HTTP请求获取图片数据
            response = requests.get(url, stream=True)
            response.raise_for_status()  # 如果响应状态码不是200，则抛出异常

            # 将图片数据写入本地文件
            with open(url_path, 'wb') as file:
                for chunk in response.iter_content(chunk_size=8192):
                    file.write(chunk)

            print(f"图片已成功下载并保存为 {url_path}")
            saveImage(mid, senderUid, url, local_path, msgKey)
            return True  # 成功后返回True
        except Exception as e:
            attempt += 1
            print(f"下载过程中出现错误 (尝试 {attempt}/{max_retries}): {e}")
            if attempt < max_retries:
                time.sleep(retry_delay)  # 等待一段时间后重试
            else:
                print(f"达到最大重试次数，下载失败: {url}")
    return False  # 达到最大重试次数后返回False


def saveImage(mid, senderUid, url , localUrl, msgKey):
    conn = db_new_connection()
    cursor = conn.cursor()
    cursor.execute('''
                    INSERT INTO biliImage (mid, senderUid, url , localUrl, msgKey)
                        VALUES (?, ?, ?, ?, ?)
                        ''',
                   (mid, senderUid, url, localUrl, msgKey))

    conn.commit()



def add_task(Item):
    print("添加任务")
    request_queue.put(Item)


def process_request(item):
    print("处理任务")
    data = select_main_user_message(item.token)
    mid = None
    if data is not None:
        mid = data['mid']
    else:
        print("data空")

    if mid is not None:
        select_user(None, mid)
    else:
        print("token过期")

    print("任务结束")

def worker():
    print("工作线程启动")
    """工作线程，从队列中获取请求并执行"""
    while True:
        request = request_queue.get()  # 从队列中获取一个请求，如果队列为空则阻塞
        if request is None:
            break  # 收到 None 表示停止工作
        try:
            if hasattr(request, 'token'):
                process_request(request)
            else:
                download_image_with_retry(request.get('mid'), request.get('senderUid'), request.get('msgKey'), request.get('url'),request.get('local_path'))
        except Exception as e:
            pass
        request_queue.task_done()
    print("工作线程退出")


threading.Thread(target=worker, name=f"Worker").start()
